/*
 * Copyright (C) 2008 Peter Kling
 *
 * This file is part of 'A Kind of Billiard', a graphical billiard simulation.
 *
 * 'A Kind of Billiard' is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 *
 * 'A Kind of Billiard' is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * 'A Kind of Billiard'. If not, see <http://www.gnu.org/licenses/>.
 */

package cg2.glowifier;

import javax.media.opengl.GL;

import cg2.misc.OpenGLDrawable;
import cg2.misc.TextureRenderer;
import cg2.shader.Shader;

/**
 * @brief Wrapper object that can be used in a preprocessing step to give several objects some kind of a glowing
 *        appearance.
 */
public class Glowifier {
	// the object that will be drawn with a glow effect
	private OpenGLDrawable drawableObject;
	
	// texture renderer
	TextureRenderer textureRenderer1 = null;
	TextureRenderer textureRenderer2 = null;
	
	// shaders used for the glow effect
	public static final String VERTEXSHADER_GAUSSIANFILTER   = "shaders/gaussianFilter_vp.cg";
	public static final String FRAGMENTSHADER_GAUSSIANFILTER = "shaders/gaussianFilter_fp.cg";
	Shader shader_gaussianFilter = null;
	
	// some parameters for configuration
	public final double GLOW_MIN;
	public final double GLOW_MAX;
	public final double GLOW_FREQUENCY;
	
	/**
	 * @brief Constructs a glowifier for the given drawable object.
	 * 
	 * The glowifier will pulsate with the specified minimal and maximal glowing intensity and with the given frequency
	 * (unit: 1/s).
	 */
	public Glowifier(OpenGLDrawable drawableObject, double glow_min, double glow_max, double glow_frequency) {
		this.drawableObject = drawableObject;
		GLOW_MIN       = glow_min;
		GLOW_MAX       = glow_max;
		GLOW_FREQUENCY = glow_frequency;
	}
	
	/**
	 * @brief Blends a glowified version of the OpenGL drawable into the frame buffer.
	 */
	public void draw(GL gl) {
		// configure pulsating effect
		double currentTime = System.currentTimeMillis()/1000.;
		double glowFactor = Math.sin(2*Math.PI*GLOW_FREQUENCY*currentTime)*(GLOW_MAX-GLOW_MIN) + (GLOW_MIN+GLOW_MAX)/2.;
		
		// store some OpenGL state variables
		gl.glPushAttrib(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT | GL.GL_POLYGON_BIT);
		
		// draw OpenGL drawable to texture
		textureRenderer1.enable(gl);
		gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
		drawableObject.draw(gl);
		textureRenderer1.disable(gl);
		
		// first pass: apply gaussian filter horizontally
		textureRenderer2.enable(gl);
		gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
		textureRenderer1.bindTexutre(gl);
		shader_gaussianFilter.enable();
		shader_gaussianFilter.setFragmentParameter("enableVertically", 0);
		shader_gaussianFilter.setFragmentParameter("glowFactor", 1.f);
		textureRenderer1.renderOnscreen(gl);
		shader_gaussianFilter.disable();
		textureRenderer2.disable(gl);
		
		// second pass:: apply gaussian filter vertically and blend result into scene
		gl.glBlendColor(1.f, 1.f, 1.f, 1.f);
		gl.glBlendFunc(GL.GL_CONSTANT_ALPHA, GL.GL_ONE);
		gl.glDisable(GL.GL_DEPTH_TEST);
		gl.glDisable(GL.GL_CULL_FACE);
		gl.glEnable(GL.GL_BLEND);
		textureRenderer2.bindTexutre(gl);
		shader_gaussianFilter.enable();
		shader_gaussianFilter.setFragmentParameter("enableVertically", 1);
		shader_gaussianFilter.setFragmentParameter("glowFactor", glowFactor);
		textureRenderer2.renderOnscreen(gl);
		shader_gaussianFilter.disable();
		
		// restore OpenGL state
		gl.glPopAttrib();
	}
	
	/**
	 * @brief Initializes this glowifier.
	 * 
	 * The glow effect will be generated with the given resolution width x height.
	 */
	public void init(GL gl, int width, int height) {
		// setup texture renderer
		textureRenderer1 = new TextureRenderer(gl);
		textureRenderer1.init(gl, width, height);
		textureRenderer2 = new TextureRenderer(gl);
		textureRenderer2.init(gl, height, width);
		
		// setup shaders
		shader_gaussianFilter = new Shader(VERTEXSHADER_GAUSSIANFILTER, FRAGMENTSHADER_GAUSSIANFILTER); 
	}
	
	/**
	 * @brief Resizes the internal buffer used to generate the glow effect.
	 */
	public void resize(GL gl, int width, int height) {
		textureRenderer1.resize(gl, width, height);
		textureRenderer2.resize(gl, width, height);
	}
}
